import os
import re
from PySide6.QtGui     import QFont, QFontMetricsF
from PySide6.QtGui     import QAction, QPen, QColor, QPainter
from PySide6.QtCore    import Qt, QModelIndex, QAbstractListModel
from PySide6.QtCore    import QPoint, Signal
from PySide6.QtWidgets import QListView, QMenu, QStyledItemDelegate
from PySide6.QtWidgets import QFileDialog


class CustomFileListModel(QAbstractListModel):
    def __init__(self, files:list=[]):
        super().__init__()
        self._data = files

    def rowCount(self, parent=QModelIndex()):
        return len(self._data)

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            return self._data[index.row()]
        return None

    # Update means clear and reset data
    def listUpdate(self, files:list):
        self.beginResetModel()
        self.endResetModel()




class CustomFileListDelegate(QStyledItemDelegate):
    def __init__(self):
        super().__init__()
        self.bgColorUsual = QColor(255, 255, 255)
        self.bgColorHover = QColor(0, 143, 191)
        self.bgColorClick = QColor(255, 110, 170)
        
        self.itemMouseHover = -1
        self.itemMouseClick = -1

    def listMouseHoverHandler(self, row):
        self.itemMouseHover = row

    def listMouseClickHandler(self, row):
        self.itemMouseClick = row

    def listMouseLeaveHandler(self):
        self.itemMouseHover = -1

    def paint(self, painter, option, index):
        painter.save()
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        painter.setRenderHint(QPainter.RenderHint.TextAntialiasing)
        painter.setPen(QPen(Qt.black, 1))

        originx = option.rect.x()
        originy = option.rect.y()
        width   = option.rect.width()
        height  = option.rect.height()

        metric = QFontMetricsF(option.font)
        ldit = ' >>> '
        rdit = ' <<< '
#       lblt = index.data(Qt.UserRole+1)
        lblt = index.data()
        ldiw = metric.horizontalAdvance(ldit)
        rdiw = metric.horizontalAdvance(rdit)
        lblw = metric.horizontalAdvance(lblt)
        icow = 20
        sepw = (width - ldiw - rdiw - lblw - icow) / 2.0
        icorect = option.rect.adjusted(ldiw+sepw, 0, lblw+rdiw+sepw, 0)
        lblrect = option.rect.adjusted(ldiw+sepw+icow, 0, sepw+rdiw, 0)
        ldirect = option.rect.adjusted(0, 0, sepw+icow+lblw+sepw+rdiw, 0)
        rdirect = option.rect.adjusted(ldiw+sepw+icow+lblw+sepw, 0, 0, 0)

        # Draw background
        if index.row() == self.itemMouseHover:
            painter.fillRect(option.rect, self.bgColorHover)
            painter.drawText(ldirect, Qt.AlignLeft|Qt.AlignVCenter, ldit)
            painter.drawText(rdirect, Qt.AlignLeft|Qt.AlignVCenter, rdit)
        else:
            painter.fillRect(option.rect, self.bgColorUsual)

        if index.row() == self.itemMouseClick:
            # Draw border
            rect = option.rect
            painter.fillRect(rect, self.bgColorClick)
            idtrectl = rect.adjusted(0, 0, -(width-3), 0)
            idtrectr = rect.adjusted(width-3, 0, 0, 0)
            painter.fillRect(idtrectl, Qt.black)
            painter.fillRect(idtrectr, Qt.black)
            painter.drawText(ldirect, Qt.AlignLeft|Qt.AlignVCenter, ldit)
            painter.drawText(rdirect, Qt.AlignLeft|Qt.AlignVCenter, rdit)
#           rect.adjust(1, 1, -1, -1)
#           painter.drawRect(rect)

        # Draw Cube
        h = 0.8 * option.rect.height()
        w = 0.7 * icow
        cw = option.rect.left() + ldiw + sepw + 0.15*icow
        ch = option.rect.top() + 0.5 * option.rect.height()
        painter.setPen(QPen(Qt.black, 2))
        painter.drawRect(cw-0.5*w, ch-h/6, 2*w/3, 2*h/3)
        point1 = QPoint(cw-0.5*w, ch-h/6)
        point2 = QPoint(cw-w/6, ch-0.5*h)
        point3 = QPoint(cw+0.5*w, ch-0.5*h)
        point4 = QPoint(cw+w/6, ch-h/6)
        painter.drawPolyline([point1, point2, point3, point4])
        point1 = QPoint(cw+0.5*w, ch-0.5*h)
        point2 = QPoint(cw+0.5*w, ch+h/6)
        point3 = QPoint(cw+w/6, ch+0.5*h)
        painter.drawPolyline([point1, point2, point3])
        painter.drawEllipse(QPoint(cw-w/6, ch+h/6), 1, 1)
        
        # Draw Text
        painter.drawText(lblrect, Qt.AlignLeft|Qt.AlignVCenter, lblt)
        painter.restore()


    def paint_old(self, painter, option, index):
        painter.save()
        painter.setPen(QPen(Qt.black, 1))

        lbrect = option.rect.adjusted(0, 0, 0, 0)
        lbtext = index.data()

        painter.drawText(lbrect, Qt.AlignLeft|Qt.AlignVCenter, lbtext)
        painter.restore()


class CustomFileList(QListView):
    signalMouseHover = Signal(int)
    signalMouseLeave = Signal()
    signalMouseClick = Signal(int)
    signalItemClicked = Signal(QModelIndex)
    def __init__(self, parent=None):
        super().__init__(parent)

        self.pattern = re.compile(r'\d+')
#       self.assembler = ['hello1', 'hello2', 'hello3']
        self.assembler = []
        self.cwd = os.path.dirname(os.path.abspath(__file__))
        self.model = CustomFileListModel(self.assembler)
        self.delegate = CustomFileListDelegate()
        self.setModel(self.model)
        self.setItemDelegate(self.delegate)

        self.setup_context()
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.popup_context)
        self.setMouseTracking(True)

        self.signalMouseHover.connect(self.delegate.listMouseHoverHandler)
        self.signalMouseClick.connect(self.delegate.listMouseClickHandler)
        self.signalMouseLeave.connect(self.delegate.listMouseLeaveHandler)

    def popup_context(self, pos):
        self.context.exec(self.mapToGlobal(pos))

    def setup_context(self):
        self.context = QMenu(self)
        add_files = QAction('Update List', self)
        add_prefix = QAction('Add Prefix', self)
        add_postfix = QAction('Add Postfix', self)
        alt_prefix = QAction('Alter Prefix', self)
        alt_postfix = QAction('Alter Postfix', self)
        alt_serial  = QAction('Alter Serial', self)

        self.context.addAction(add_files)
        self.context.addSeparator()
        self.context.addAction(alt_serial)
        self.context.addSeparator()
        self.context.addAction(add_prefix)
        self.context.addAction(add_postfix)
        self.context.addSeparator()
        self.context.addAction(alt_prefix)
        self.context.addAction(alt_postfix)

        add_files.triggered.connect(self.update_list_files)
        alt_serial.triggered.connect(self.alter_list_serial)
        add_prefix.triggered.connect(self.add_list_prefix)
        add_postfix.triggered.connect(self.add_list_postfix)
        alt_prefix.triggered.connect(self.alter_list_prefix)
        alt_postfix.triggered.connect(self.alter_list_postfix)

    def leaveEvent(self, event):
        self.signalMouseLeave.emit()

    def mouseMoveEvent(self, event):
        index = self.indexAt(event.position().toPoint())
        self.signalMouseHover.emit(index.row())
        start = self.model.index(0)
        stop  = self.model.index(self.model.rowCount())
        self.model.dataChanged.emit(start, stop)
        super().mouseMoveEvent(event)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            clicked = self.indexAt(event.position().toPoint())
            self.signalMouseClick.emit(clicked.row())
            self.model.dataChanged.emit(clicked, clicked)
            self.signalItemClicked.emit(clicked)
        super().mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            clicked = self.indexAt(event.position().toPoint())
            self.model.dataChanged.emit(clicked, clicked)
        super().mouseReleaseEvent(event)


    def update_list_files(self):
        print("curr dir: ", self.cwd)
        path = QFileDialog.getExistingDirectory(self, 'Select', self.cwd)
        if not path:
            return

        print("path selected: ", path)
        self.cwd = path
        self.assembler.clear()
        for file in os.listdir(self.cwd):
            if not file.endswith('fCal') and not file.endswith('pCal') and not file.endswith('pfCal'):
                continue
            self.assembler.append(file)
        self.model.listUpdate(self.assembler)


    def alter_list_serial(self):
        print("alter serial")

    def add_list_prefix(self):
        print("add prefix")

    def add_list_postfix(self):
        print("add postfix")

    def alter_list_prefix(self):
        print("alter prefix")

    def alter_list_postfix(self):
        print("alter postfix")


#from PySide6.QtWidgets import QApplication
#app = QApplication()
#files = CustomFileList()
#files.show()
#app.exec()
